package edu.imac.angryfood.states;

import java.util.ArrayList;


import org.newdawn.slick.AngelCodeFont;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.gui.AbstractComponent;
import org.newdawn.slick.gui.ComponentListener;
import org.newdawn.slick.gui.MouseOverArea;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.state.transition.FadeInTransition;
import org.newdawn.slick.state.transition.FadeOutTransition;

import edu.imac.angryfood.component.AbstractProjectile;
import edu.imac.angryfood.component.BombProjectile;
import edu.imac.angryfood.game.Angryfood;
import edu.imac.angryfood.render.Camera;
import edu.imac.angryfood.render.ShapesRender;
import edu.imac.angryfood.utils.ContactListenerModified;
import edu.imac.angryfood.utils.IOJDom;
import edu.imac.angryfood.utils.ParserXML;
import edu.imac.angryfood.world.Body;
import edu.imac.angryfood.world.KeyboardInputs;
import edu.imac.angryfood.world.World;

/**
 * Main state where the game is taking place
 * 
 */
public class GameState extends BasicGameState implements ComponentListener {

	public static final int UPDATES_PER_SECOND = 20;
	public static final int ITERATIONS = 30;
	public static final int UPDATES = 3;
	public static final int RENDERMODE = 3;

	public static int ID;
	private Angryfood parent; 
	public World world; 
	public ShapesRender render; 
	public KeyboardInputs input;
	private Camera camera;
	
	private int mouseX;
	private int mouseY;
	private boolean clicked;

	private Body CurrentBody;
	private int score;
	private boolean testBool;
	private boolean targets;
	private boolean projectiles;
	private MouseOverArea[] areas;
	

	private int numChap;
	private int numLevel;
	private int flag_change;
	AngelCodeFont maPolice;
	String score02;

	private Image end, back, again, resume, finish, slingShotPiece;
	private StateBasedGame game;
	private GameContainer container;

	/** 
	 * Constructor of the main state
	 * @param parent game parent
	 * @param ID id of the state
	 * @param numChap number of the chapiter
	 * @param numLevel number of the level
	 */
	public GameState(Angryfood parent, int ID, int numChap, int numLevel) {
		super();
		GameState.ID = ID;
		this.parent = parent;
		render = new ShapesRender(RENDERMODE);
		clicked = false;
		this.numChap = numChap ;
		this.numLevel = numLevel;
	}
	
	public int getID() {
		return GameState.ID;
	}

	/**
	 * initializes the the textures
	 */
	private void initTextures() {
		try {
			end		= new Image("res/img/game/fin.png");
			resume	= new Image("res/img/game/continue.png");
			finish	= new Image("res/img/game/win.png");
			back		= new Image("res/img/menu/back.png");
			again		= new Image("res/img/game/again.png");
			slingShotPiece = new Image("res/img/game/morceauLancePierre.png").getFlippedCopy(true,false);
		} catch (SlickException e) {
			e.printStackTrace();
		}
	}
	
	/** initializes the game
	 * @param container contains the game
	 * @param game the game
	 * @throws SlickException
	 */

	public void init(GameContainer container, StateBasedGame game) throws SlickException {
		flag_change =0;
		initialisation(container);
		this.game = game;
		this.container =container;

		// Listener for contacts
		org.jbox2d.dynamics.World mworld = world.getJBoxWorld();
		mworld.setContactListener(new ContactListenerModified());
		
		for(int i=0; i<world.getBodyCount(); i++)
		{
			world.getBody(i).setInitLife(world.getBody(i).getLife());
		}
	
		score = 0;
		testBool = false;
		maPolice = new AngelCodeFont("res/font/ComicBook.fnt", "res/font/ComicBook_0.tga");
		score02 = "";

	}
	

	/** render the graphics in slick
	 * @param container contains the game
	 * @param game the game
	 * @param g the graphics
	 */
	public void render(GameContainer container, StateBasedGame game, Graphics g)
			throws SlickException {

		
		if(parent.load_level == false)
		{
			parent.load_level = true;
			initialisation(container);
			
		}		
		
		g.setLineWidth(1);
		g.drawImage(world.skyImage,0,0,800,600,0,0,800,600);
		g.pushTransform();
			
			// bg
	
			for (AbstractProjectile proj : world.getProjectiles()){
				//we take the current projectile
				if (proj.getNum()==camera.getProjFollowed()){	
					if(proj.isDropped() && (camera.getScale()>=1.f)) {
						camera.followBody(proj);
					} 
				}
				
			}
			// camera
			camera.display(container, g); 
			int x1 = (int) (camera.getX()/5);
			if(x1<0) x1=0;
			int y1 = (0);
			int x2 = (int) (2285);
			int y2 = (int) (600);
			g.drawImage(world.backgroundImage, -400, -200,1800,300, x1,y1,x2,y2);
			
			// sling shot
			if(clicked==true){
				g.setLineWidth(10);
				g.setAntiAlias(true);
				g.drawGradientLine(AbstractProjectile.xInitXML+5,AbstractProjectile.yInitXML, new Color(86,21,18),(mouseX)-380-20, (-mouseY)+420+AbstractProjectile.yInitXML, new Color(144,27,18));
			}	

			for (int i = 0; i < world.getBodyCount(); i++) {
				render.drawBody(g, world.getBody(i));
			}

			int xx,yy;
			xx=14;
			yy=17;
			
			if(clicked==true){
				g.setLineWidth(10);
				g.setAntiAlias(true);
				g.drawGradientLine(AbstractProjectile.xInitXML-15,AbstractProjectile.yInitXML+5, new Color(86,21,18),(mouseX)-380-20, (-mouseY)+420+AbstractProjectile.yInitXML, new Color(144,27,18));
			}	
			g.drawImage(slingShotPiece, AbstractProjectile.xInitXML+xx, AbstractProjectile.yInitXML+yy,AbstractProjectile.xInitXML-37+xx, AbstractProjectile.yInitXML-106+yy, 0, 0, 37, 106);
			
		g.popTransform();

		hurtAndScore(container,g);
		/** score */
		g.setFont(maPolice);
		g.setColor(Color.white);
		score02 = String.valueOf(score);
		g.drawString(score02, 705 , 30); 

	}
	

	/** update the physic world
	 * @param container the game container
	 * @param game the game
	 * @param delta
	 */	
	public void update(GameContainer container, StateBasedGame game, int delta)
			throws SlickException {
		
		for (int i = 0; i < UPDATES; i++) 
			world.update(1f / UPDATES_PER_SECOND);
		
		for (int i = 0; i < UPDATES; i++) 
			world.update(1f / UPDATES_PER_SECOND);
		
		if (input.getInput().isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)){
			input.mouseDown(world, container, camera);
			for (AbstractProjectile proj : world.getProjectiles()){
				//we take the current projectile
				if (proj.getNum()==AbstractProjectile.current){	
					if(proj.getShapeType().equals("Circle"))
						mouseX = (int)(400 + proj.getX() - proj.getRadius() - 2);
					if(proj.getShapeType().equals("Rectangle"))
						mouseX = (int)(400 + proj.getX() - proj.getWidth()/2 - 2);
					mouseY = (int)(300 - proj.getY() + 7);
				}
				
			}
			clicked = true;
		}
		else {
			input.mouseUp(world, camera);
			clicked = false;
		}
		
		//we remove the sleeping projectiles
		for (AbstractProjectile proj : world.getProjectiles()){
			if (proj.isSleeping() && proj.getClicked()){
				world.remove(proj);
			}
			//and the fragments of the bomb
			if (proj instanceof BombProjectile){
				BombProjectile bomb=(BombProjectile) proj;
				if (bomb.getClicked()){
					bomb.checkFragments(world);
				}
			}
		}
		
		//checking for collisions between projectiles and blocks
		for(int i=0; i<world.getBodyCount(); i++){		 
			if(world.getBody(i).isStatic() == false){			
				for(int j=0; j<world.getJBoxWorld().retournerShapes().size(); j++){				 		
					if(world.getJBoxWorld().getShape(j).getBody() == world.getBody(i).getJboxBody()){
						if(world.getBody(i).getType() == "PROJECTILE"){
							AbstractProjectile proj=(AbstractProjectile) world.getBody(i);
							if (proj.getNum()==AbstractProjectile.current && !proj.getClicked()){
								//System.out.println("collision");
								proj.collision(world);
							}
						}
					}		
				}
			}
		}

		
		if(input.getInput().isKeyPressed(Input.KEY_R)){
			initialisation(container);
			score = 0;
			testBool = false;
		}
		if (input.getInput().isKeyDown(Input.KEY_LEFT)) {
			camera.deplaX(-1);
		}
		if (input.getInput().isKeyDown(Input.KEY_RIGHT)) {
			camera.deplaX(1);
		}
		if (input.getInput().isKeyDown(Input.KEY_UP)) {
			camera.zoom(1);
		}
		if (input.getInput().isKeyDown(Input.KEY_DOWN)) {
			camera.zoom(-1);
		}
		if (input.getInput().isKeyDown(Input.KEY_SPACE)) {
			if(camera.getScale()<=1.f)
				camera.setPosition(0,0,1.f);
			else
				camera.setPosition(0,camera.getY(),camera.getScale());
			camera.setProjFollowed(AbstractProjectile.current);
		}
	}
	
	/** function that's called when the mouse is pressed
	 * @param button the button that's pressed
	 * @param x the x coordinate of the mouse
	 * @param y the y coordinate of the mouse
	 */
	
	public void mousePressed(int button, int x, int y){		
		for (AbstractProjectile proj : world.getProjectiles()){
			if (proj.getDroped()){
				if (!proj.getClicked()){					
					if (AbstractProjectile.current==proj.getNum()){

						try {
							proj.click(world);
						} catch (SlickException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			}
		}
	}
	
	/** initialisation of the game
	 * 
	 * @param container container of the game
	 */
	
	public void initialisation(GameContainer container){
		areas = new MouseOverArea[5];
		
		areas[0] = new MouseOverArea(container, end, 250, 150 , 300, 150, this);
		areas[0].setNormalColor(new Color(1,1,1,0.8f));
		areas[0].setMouseOverColor(new Color(1,1,1,1f));
		areas[1] = new MouseOverArea(container, again, 400, 300 , 200, 50, this);
		areas[1].setNormalColor(new Color(1,1,1,0.8f));
		areas[1].setMouseOverColor(new Color(1,1,1,1f));
		areas[2] = new MouseOverArea(container, back, 125, 300 , 250, 50, this);
		areas[2].setNormalColor(new Color(1,1,1,0.8f));
		areas[2].setMouseOverColor(new Color(1,1,1,1f));
		areas[3] = new MouseOverArea(container, resume, 400, 300 , 200, 50, this);
		areas[3].setNormalColor(new Color(1,1,1,0.8f));
		areas[3].setMouseOverColor(new Color(1,1,1,1f));
		areas[4] = new MouseOverArea(container, finish, 250, 150 , 300, 150, this);
		areas[4].setNormalColor(new Color(1,1,1,0.8f));
		areas[4].setMouseOverColor(new Color(1,1,1,1f));
		this.input = new KeyboardInputs(container.getInput());
		this.camera = new Camera(0, 0, 1f);
		initTextures();

		// world
		world = new World(-800, -500, 2400, 5000, -10f, ITERATIONS);

		// Read xml

		if(parent.load_level == true && flag_change==0){
			ArrayList<ParserXML.Material> materialList = ParserXML.ReadMaterialStyle("res/description/materialStyle.xml");
			ArrayList<ParserXML.BlockStyle> blockStyleList = ParserXML.ReadBlockStyle("res/description/blockStyle.xml", materialList);
			ParserXML.XMLLevel(parent.currentLevel, blockStyleList, world);
		}
		
		if(flag_change==1){

			ArrayList<ParserXML.Material> materialList = ParserXML.ReadMaterialStyle("res/description/materialStyle.xml");
			ArrayList<ParserXML.BlockStyle> blockStyleList = ParserXML.ReadBlockStyle("res/description/blockStyle.xml", materialList);
			ParserXML.XMLLevel("res/levels/level"+numChap+"_"+numLevel+".xml", blockStyleList, world);
			flag_change =0;
		}

		AbstractProjectile.setCurrent(0);
		
		org.jbox2d.dynamics.World mworld = world.getJBoxWorld();
		mworld.setContactListener(new ContactListenerModified());


	}
	
	/**
	 * computes the scores and checks the contacts between the blocs
	 * @param container contains the game
	 * @param g the graphics
	 */
	
		public void hurtAndScore(GameContainer container, Graphics g){
			if(testBool == false) {

				java.util.TimerTask maTask01 = new java.util.TimerTask()
				{
					public void run(){
						testBool = true;
						this.cancel();
					}
				};
				new java.util.Timer().schedule(maTask01, 1500);
			}
					
			if(testBool == true){
				for(int i=0; i<world.getBodyCount(); i++) {			
					if(world.getBody(i).isStatic() == false) {
					
						targets = isThereTargets();
						projectiles = isThereProjectiles();
						
						/** player wins */
						if(targets == false){
							ParserXML.updateScoreIfBest("level"+numChap+"_"+numLevel+".xml", score);
							IOJDom.modifScoreLevel(parent.getCurrentPlayer().getName(), score, numChap+"_"+numLevel);
							// the player goes into the next level
							
								if((numChap == 2) && (numLevel ==3)){
									areas[0].render(container, g);
									areas[1].render(container, g);
									areas[2].render(container, g);
								}
								else{
									areas[2].render(container, g);
									areas[3].render(container, g);
									areas[4].render(container, g);
									g.drawString(""+score, 450 , 250);
								}					
							}							
						}

						/** player looses */
						if(projectiles == false && targets == true){
							areas[0].render(container, g);
							areas[1].render(container, g);
							areas[2].render(container, g);					
							
						}
						
						/** TARGET */
						if(world.getBody(i).getType() == "TARGET"){

							if(world.getBody(i).getLife() == 0) {											
								world.getBody(i).setImg(2, 0);
								
								CurrentBody=world.getBody(i);
								java.util.TimerTask maTask= new java.util.TimerTask()
								{
									public void run(){
										if(CurrentBody!=null)
										{
											score += 200;
											world.remove(CurrentBody);
											CurrentBody=null;
										}
										this.cancel();
									}
								};
								
								new java.util.Timer().schedule(maTask, 900);
							}else{
								if(world.getBody(i).getLife() <= 0) {
									world.getBody(i).setLife(0);
								}
																	
								if(world.getBody(i).getLife() <= (world.getBody(i).getInitLife()/2)) {
									world.getBody(i).setImg(1, 0);
								}
							}							
						}					
						
						/** BLOCK */
						
						if(world.getBody(i).getType() == "BLOCK"){
							
							if(world.getBody(i).getLife() == 0) {					
								world.remove(world.getBody(i));
								score += 80;
								
							}else{
								if(world.getBody(i).getLife() <= 0) {
									world.getBody(i).setLife(0);
								}
								
								if(world.getBody(i).getLife() <= (world.getBody(i).getInitLife()/2)) {
									world.getBody(i).setImg(1, 0);
								}
							}
						}				
					
					
					for(int j=0; j<world.getJBoxWorld().retournerShapes().size(); j++) {				
													
						if(world.getJBoxWorld().getShape(j).getBody() == world.getBody(i).getJboxBody()) {
							
							// all the bodies non static
							if(world.getBody(i).isStatic() == false) {						
								if(world.getBody(i).getLife() > 0) {

									double normeBefore = world.getBody(i).getLinearVelocity().x * world.getBody(i).getLinearVelocity().x + world.getBody(i).getLinearVelocity().y*world.getBody(i).getLinearVelocity().y;
									double norme = Math.sqrt(normeBefore);
									score += norme;
									int perteVie = (int) ((norme) + 4)* 3;				
									world.getBody(i).loseLife(perteVie);
								}
							}
						}
					}
				}
			}
		}
		
		/**
		 * Returns true if there are targets
		 * @return boolean : true if there are targets on scene
		 */
		
		public boolean isThereTargets() {
			int count = 0;
			for(int i=0; i<world.getBodyCount(); i++) {			
				if(world.getBody(i).isStatic() == false) {
					
					if(world.getBody(i).getType() == "TARGET"){
						count += 1;
					}
				}
			}
			if(count != 0){
				return true;
			}else {
				return false;
			}
		}
		/**
		 * Returns true if there are projectiles
		 * @return boolean : true if there are projectiles on scene
		 */
		
		public boolean isThereProjectiles() {
			if (AbstractProjectile.current>=AbstractProjectile.nbProj){
				return false;
			}
			else {				
				return true;
			}
		}
		
		/**
	     * Function used to click on the areas
	     * 
	     * @param source Area of the screen which is clicked
	     */

		public void componentActivated(AbstractComponent source) {	
			
				AbstractProjectile.nbProj=0;
			
				if (source == areas[1]) {
					flag_change =1;
					parent.load_level = false;
					initialisation(container);
					score = 0;
					testBool = false;

				}
				
				if (source == areas[2]) {
					parent.load_level = false;
					game.enterState(3, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
					
				}
			
				if (source == areas[3]) {
					if(numLevel==3){
						numChap++;
						numLevel=0;
					}
					numLevel++;
					flag_change =1;
					//parent.load_level = false;
					initialisation(container);
					score = 0;
					testBool = false;
				}
				

		}

	
}

